/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// type::empty_t
#include "type/typetools.h"
// NX_LOOP
#include "macro/macro.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    Operators
*/

template <typename T, typename Base_ = type::empty_t>
struct Unequal : Base_
{
    // Need operator==
    friend bool operator!=(const T& x, const T& y) { return (!static_cast<bool>(x == y)); }
};

template <typename T, typename Base_ = type::empty_t>
struct Alike : Base_
{
    // Need operator<
    friend bool operator!=(const T& x, const T& y) { return (static_cast<bool>(x < y) && static_cast<bool>(y < x)); }
    friend bool operator==(const T& x, const T& y) { return !(x != y); }
};

template <typename T, typename Base_ = type::empty_t>
struct Comparable : Base_
{
    // Need operator<
    friend bool operator> (const T& x, const T& y) { return ( static_cast<bool>(y < x)); }
    friend bool operator<=(const T& x, const T& y) { return (!static_cast<bool>(x > y)); }
    friend bool operator>=(const T& x, const T& y) { return (!static_cast<bool>(x < y)); }
};

template <typename T, typename V = int, typename Base_ = type::empty_t>
struct Operable : Base_
{
    /*
        Must be the end of the nx_operator's parameters, 
        or you need explicitly specify the V parameter manually
    */

    // Need operator+=

    friend T& operator-=(T& x, const V& v) { return x += -v; }

    friend T& operator+ (T& x, const V& v) { return x += v; }
    friend T& operator+ (const V& v, T& x) { return x += v; }
    friend T& operator- (T& x, const V& v) { return x -= v; }
    friend T& operator- (const V& v, T& x) { return x -= v; }

    friend T& operator++(T& x)             { return x += 1; }
    friend T  operator++(T& x, int)        { T nrv(x); ++x; return nrv; }
    friend T& operator--(T& x)             { return x -= 1; }
    friend T  operator--(T& x, int)        { T nrv(x); --x; return nrv; }
};

/*
    nx_operator macro

    nx_operator(T, nx::Unequal, nx::Comparable, nx::Operable)
    -->
    nx::Unequal<T, nx::Comparable<T, nx::Operable<T > > >

    nx_operatorex(T, double, nx::Unequal, nx::Comparable, nx::Operable)
    -->
    nx::Unequal<T, nx::Comparable<T, nx::Operable<T, double > > >
*/

#define NX_OPERATOR_E(args, i, ...)  NX_ARGS_F2(args)<NX_ARGS_F1(args) >
#define NX_OPERATOR_F(args, i, nxt)  NX_ARGS_F2(args)<NX_ARGS_F1(args), nxt >
#define NX_OPERATOR_I(args)          NX_ARGS_F1(args),NX_ARGS_L2(args)

#define nx_operatorex(T, P, ...) \
    NX_LOOP(NX_CONTER(__VA_ARGS__), NX_OPERATOR_F, NX_OPERATOR_I, NX_OPERATOR_F, P, T, __VA_ARGS__)

#define nx_operator(T, ...) \
    NX_LOOP(NX_CONTER(__VA_ARGS__), NX_OPERATOR_F, NX_OPERATOR_I, NX_OPERATOR_E, , T, __VA_ARGS__)

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
